`include "defines.v"
module ifu(
  input clk  , 
  input rst_n,
  input [`VADDR_W-1: 0] boot_addr,
  input stall,
  // 地址重定向
  input                  redict_valid_i,
  input [`VADDR_W-1:0]   redict_pc_i   ,
  input [`BRU_MSG_W-1:0] bru_message   ,
  // icache
  output                  ifu_icache_valid_o   ,
  input                   icache_ifu_ready_i   ,
  output [`VADDR_W-1  :0] ifu_icache_req_addr_o,
  output [`BPU_PRE_W-1:0] ifu_icache_predict_o 
);
// 更新的关键在于 
// 空间上commit 越近 跳转猜测正确的概率越大
// 时间上 越老的信号正确性越高
// 预测地址 错误也不会造成正确性问题 因为会在BRU阶段修正
reg   [`VADDR_W-1:0] pc ;
wire  [`VADDR_W-1:0] npc;
wire                 bpu_predict_valid;
wire  [`VADDR_W-1:0] bpu_predict_pc   ;

wire pcUpdate   = ifu_icache_valid_o && icache_ifu_ready_i;

// process counter update
always@(posedge clk or  negedge rst_n)
  if(~rst_n)
    pc <= boot_addr;
  else if(pcUpdate || redict_valid_i || bpu_predict_valid)
    pc <= npc;

// jump-entry 是最早的刷新信息 所以优先级最高
// 重定向刷新 大于 分支刷新
// 最后才是 PC的下一条指令地址 jump_entry[`VADDR_W] ? jump_entry[`VADDR_W-1:0] : 
// PC重定向后恢复4指令（4*4B）对齐  加入压缩指令后 会发生2B的跳转 首先2B对齐 然后4B对齐 
function [`VADDR_W-1:0] PcAlignment;
  input [`VADDR_W-1:0] pc;
  begin
    PcAlignment[`VADDR_W-1:4] = pc[`VADDR_W-1:4];
    // PcAlignment[3:2] = pc[1] ? pc[3:2] :2'b00;
    PcAlignment[3:2] = 2'b00;
    // PcAlignment[3:2] = pc[3:2] != 2'b00 ? 2'b00 :pc[3:2];
    PcAlignment[1:0] = 2'b00;
  end
endfunction

assign npc = redict_valid_i       ? redict_pc_i              :
             bpu_predict_valid    ? bpu_predict_pc           :
             PcAlignment(pc) + 'd16;

reg req_valid;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    req_valid <= 1'b0;
  else 
    req_valid <= 1'b1;

// Icache
assign ifu_icache_valid_o     = req_valid;
assign ifu_icache_req_addr_o  = pc;
assign ifu_icache_predict_o   = {bpu_predict_valid,bpu_predict_pc}; 

bpu BPU(
  .clk   (clk  ),
  .rst_n (rst_n),

  .npc         (npc),
  .bru_message (bru_message),
  
  .predict_valid(bpu_predict_valid),
  .predict_pc   (bpu_predict_pc   )
);

endmodule